﻿using DB2WORD.Logics;
using DB2WORD.Models;
using System.Data;
using System.Text.RegularExpressions;

namespace DB2WORD
{
    public partial class FrmDetail : Form
    {
        public FrmDetail()
        {
            InitializeComponent();
            this.Shown += this.FrmDetail_Load;
            this.SetDoubleBuffer();
            this.pnlNames.SetDoubleBuffer();
            this.pnlSelect.Enabled = false;
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;
                return cp;
            }
        }

        private async void FrmDetail_Load(object? sender, EventArgs e)
        {
            this.txtDbName.TextChanged += this.ProjectChanged;
            this.txtServer.TextChanged += this.ProjectChanged;
            this.txtUser.TextChanged += this.ProjectChanged;
            this.txtPwd.TextChanged += this.ProjectChanged;
            this.txtRegexes.TextChanged += this.ProjectChanged;
            this.btnSelectAll.Click += this.BtnSelectAll_Click;
            this.btnSelectReverse.Click += this.BtnSelectReverse_Click;
            this.btnSelectRegex.Click += this.BtnSelectRegex_Click;
            this.btnUnSelectRegex.Click += this.BtnUnSelectRegex_Click;
            this.btnFind.Click += this.BtnFind_Click;
            this.btnIgnoreCase.Click += this.BtnIgnoreCase_Click;
            this.btnSave.Click += this.BtnSave_Click;
            this.lblCount.Text = "";
            this.SetTooltip();
        }

        private async void BtnSave_Click(object? sender, EventArgs e)
        {
            this.btnSave.Enabled = false;
            await Task.Run(() => this.Run());
            this.btnSave.Delay();
        }

        private void SetTooltip()
        {
            MainForm.Self.toolTip.SetToolTip(this.btnSelectAll, "全部选中");
            MainForm.Self.toolTip.SetToolTip(this.btnSelectReverse, "全部反选");
            MainForm.Self.toolTip.SetToolTip(this.btnFind, "根据正则表达式查找");
            MainForm.Self.toolTip.SetToolTip(this.btnSelectRegex, "选中所有符合正则表达式的表");
            MainForm.Self.toolTip.SetToolTip(this.btnUnSelectRegex, "取消选中所有符合正则表达式的表");
            MainForm.Self.toolTip.SetToolTip(this.btnIgnoreCase, "忽略大小写");
        }

        private bool isShown = false;
        private bool isCaseSensitive = false;
        private string? lastID = null;

        public void ShowFrom()
        {
            MainForm.Project.IsModified = false;
            this.isShown = false;
            if(lastID == MainForm.Project.ID)
            {
                this.isShown = true;
                return;
            }
            this.isCaseSensitive = false;
            this.btnIgnoreCase.BackColor = SystemColors.Control;
            this.txtDbName.Text = MainForm.Project.DBSetting?.DbName;
            this.txtServer.Text = MainForm.Project.DBSetting?.Server;
            this.txtUser.Text = MainForm.Project.DBSetting?.User;
            this.txtPwd.Text = MainForm.Project.DBSetting?.Password;
            this.txtRegexes.Text = MainForm.Project.Regexes;
            this.pnlNames.Controls.Clear();
            this.isShown = true;
        }

        private async void ProjectChanged(object sender, EventArgs e)
        {
            if(!this.isShown)
            {
                return;
            }
            MainForm.Project.DBSetting.Server = this.txtServer.Text.Trim();
            MainForm.Project.DBSetting.DbName = this.txtDbName.Text.Trim();
            MainForm.Project.DBSetting.User = this.txtUser.Text.Trim();
            MainForm.Project.DBSetting.Password = this.txtPwd.Text;
            MainForm.Project.Regexes = this.txtRegexes.Text.Trim();
            MainForm.Project.IsModified = true;
        }

        private async void btnTest_Click(object sender, EventArgs e)
        {
            this.btnTest.Enabled = false;
            this.btnSave.Enabled = false;
            MainForm.Self.SetMessage("正在加载表名称列表...");
            Application.DoEvents();
            var rst = await GetTableNames();
            if(!rst.IsSuccess)
            {
                MainForm.Self.SetMessage($"数据库连接失败：{rst.Message}");
                MessageBox.Show(this, $"数据库连接失败：\r\n{rst.Message}", "提示");
            }
            this.btnSave.Enabled = true;
            await this.btnTest.Delay();
        }

        public const string SQL_SERVER_FORMAT = "data source={0};initial catalog={1};user id={2};password={3};max pool size=512;MultipleActiveResultSets=True";

        private async Task<Result> GetTableNames()
        {
            var result = await Task.Run(() =>
            {
                var connectString = string.Format(SQL_SERVER_FORMAT,
                    txtServer.Text.Trim(),
                    txtDbName.Text.Trim(),
                    txtUser.Text.Trim(),
                    txtPwd.Text.Trim());
                var rst = Executor.GetTableNames(connectString);
                return rst;
            });

            var regStrings = txtRegexes.Text.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            var regexes = regStrings.Select(x => new Regex(x, RegexOptions.IgnoreCase | RegexOptions.Compiled)).ToList();

            if(result.IsSuccess)
            {
                var names = result.As<List<TableInfo>>();
                if(names == null || names.Count < 1)
                {
                    return Result.Fail("没找到复合条件的表");
                }
                var list = names.Take(0).ToList();
                foreach(var name in names)
                {
                    if(regexes.Any(x => x.IsMatch(name.Name)))
                    {
                        continue;
                    }

                    list.Add(name);
                }
                if(this.InvokeRequired)
                {
                    this.Invoke(() => RefreshNames(list));
                }
                else
                {
                    RefreshNames(list);
                }
                Application.DoEvents();
            }

            return result;
        }

        private void RefreshNames(List<TableInfo> names)
        {
            var maxName = names.OrderByDescending(x => x.Name.Length).FirstOrDefault().Name;
            var gp = this.pnlNames.CreateGraphics();
            var font = new Font(this.Font.FontFamily, this.Font.Size - 1);
            var fontSize = gp.MeasureString(maxName, font);
            var width = (int)fontSize.Width + 28;
            var height = (int)fontSize.Height + 5;
            this.pnlSelect.Enabled = false;
            this.pnlNames.Enabled = false;
            this.pnlNames.Controls.Clear();
            Application.DoEvents();
            var count = 0;
            var allCount = 0;
            foreach(var item in names)
            {
                var chked = MainForm.Project.TableNames?.Contains(item.Name, StringComparer.OrdinalIgnoreCase) == true;
                var chk = new CheckBox {
                    Width = width,
                    Height = height,
                    Text = item.Name,
                    Checked = chked,
                    Tag = item,
                    Font = font,
                    TextAlign = ContentAlignment.BottomLeft,
                    Padding = new Padding(0),
                    Margin = new Padding(2, 0, 0, 0)
                };
                chk.BackColor = chk.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
                chk.CheckedChanged += this.Chk_CheckedChanged;
                pnlNames.Controls.Add(chk);
                allCount++;
                if(chked)
                {
                    count++;
                }
                lblCount.Text = $"( {count} / {allCount} )";
                Application.DoEvents();
            }
            this.pnlNames.Enabled = true;
            this.pnlSelect.Enabled = allCount > 0;
            MainForm.Self.SetMessage($"{allCount} 个表名称加载完成");
            Application.DoEvents();
        }

        private async void Chk_CheckedChanged(object? sender, EventArgs e)
        {
            if(!this.isShown)
            {
                return;
            }
            if(sender is CheckBox chk)
            {
                chk.BackColor = chk.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
            }
            this.RefreshNameCheckboxs();
        }

        private void RefreshNameCheckboxs()
        {
            var allCount = this.pnlNames.Controls.OfType<CheckBox>().Count();
            var count = this.pnlNames.Controls.OfType<CheckBox>().Count(x => x.Checked);
            this.lblCount.Text = $"( {count} / {allCount} )";
            if(this.lastBox != null)
            {
                this.lastBox.BackColor = this.lastBox.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
                this.lastBox.ForeColor = this.pnlNames.ForeColor;
            }
        }


        public void Run()
        {
            var tables = this.pnlNames.Controls.OfType<CheckBox>().Where(x => x.Checked).Select(x => x.Tag as TableInfo).ToList();

            if(tables.Count < 1)
            {
                MessageBox.Show(this, $"请先选择要导出的表", "提示");
                return;
            }
            var checkedNames = tables.Select(x => x.Name).ToList();
            if(MainForm.Project.TableNames==null || !checkedNames.SequenceEqual(MainForm.Project.TableNames))
            {
                MainForm.Project.TableNames = checkedNames;
                MainForm.Project.IsModified = true;
                MainForm.Self.Save();
            }

            var connectionString = string.Format(SQL_SERVER_FORMAT,
                txtServer.Text.Trim(),
                txtDbName.Text.Trim(),
                txtUser.Text.Trim(),
                txtPwd.Text.Trim());

            Executor.MessageCallback = MainForm.Self.SetMessage;
            var result = Executor.Run(MainForm.Project.Name, connectionString, tables);

            if(!result.IsSuccess)
            {
                MessageBox.Show(this, $"出现错误：\r\n{result.Message}", "提示");
            }
            else
            {
                var da = MessageBox.Show(this, $"更新完成，是否去维护？", "提示", MessageBoxButtons.YesNo);
                if(da == DialogResult.Yes)
                {
                    MainForm.Self.Edit(MainForm.Project, result.As<List<GroupInfo>>());
                }
            }
        }

        private async void BtnSelectAll_Click(object? sender, EventArgs e)
        {
            this.btnSelectAll.Enabled = false;
            var chks = this.pnlNames.Controls.OfType<CheckBox>();
            foreach(var chk in chks)
            {
                chk.Checked = true;
                chk.BackColor = chk.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
            }
            this.Refresh();
            await this.btnSelectAll.Delay();
        }

        private async void BtnSelectReverse_Click(object? sender, EventArgs e)
        {
            this.btnSelectReverse.Enabled = false;
            var chks = this.pnlNames.Controls.OfType<CheckBox>();
            foreach(var chk in chks)
            {
                chk.Checked = !chk.Checked;
                chk.BackColor = chk.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
            }
            this.Refresh();
            await this.btnSelectReverse.Delay();
        }
        private async void BtnSelectRegex_Click(object? sender, EventArgs e)
        {
            this.btnSelectRegex.Enabled = false;
            var chks = this.pnlNames.Controls.OfType<CheckBox>();
            var regex = new Regex(txtRegex.Text.Trim(), RegexOptions.Compiled);
            foreach(var chk in chks)
            {
                if(regex.IsMatch(chk.Text))
                {
                    chk.Checked = true;
                    chk.BackColor = chk.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
                }
            }
            this.Refresh();
            this.btnSelectRegex.Delay();
        }

        private async void BtnUnSelectRegex_Click(object? sender, EventArgs e)
        {
            this.btnUnSelectRegex.Enabled = false;
            var chks = this.pnlNames.Controls.OfType<CheckBox>();
            var regex = new Regex(txtRegex.Text.Trim(), RegexOptions.Compiled);
            foreach(var chk in chks)
            {
                if(regex.IsMatch(chk.Text))
                {
                    chk.Checked = false;
                    chk.BackColor = chk.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
                }
            }
            this.Refresh();
            this.btnUnSelectRegex.Delay();
        }

        private int lastFind = -1;
        private CheckBox lastBox = null;
        private async void BtnFind_Click(object? sender, EventArgs e)
        {
            this.btnFind.Enabled = false;
            var chks = this.pnlNames.Controls.OfType<CheckBox>().ToList();
            var regexOptions = RegexOptions.Compiled;
            if(!this.isCaseSensitive)
            {
                regexOptions |= RegexOptions.IgnoreCase;
            }
            var regex = new Regex(txtRegex.Text.Trim(), regexOptions);
            var start = lastFind;

            for(int i = start + 1; i <= chks.Count + start; i++)
            {
                var index = i % chks.Count;
                var chk = chks[index];
                if(regex.IsMatch(chk.Text))
                {
                    if(this.lastBox != null)
                    {
                        this.lastBox.BackColor = this.lastBox.Checked ? SystemColors.ActiveCaption : this.pnlNames.BackColor;
                        this.lastBox.ForeColor = this.pnlNames.ForeColor;
                    }

                    this.pnlNames.Select();
                    this.pnlNames.AutoScrollOffset = chk.Location;
                    chk.Select();
                    chk.BackColor = SystemColors.Highlight;
                    chk.ForeColor = SystemColors.HighlightText;
                    Application.DoEvents();
                    this.lastFind = index;
                    this.lastBox = chk;
                    break;
                }
            }
            await this.btnFind.Delay(100);
        }

        private async void BtnIgnoreCase_Click(object? sender, EventArgs e)
        {
            this.isCaseSensitive = !this.isCaseSensitive;
            this.btnIgnoreCase.BackColor = this.isCaseSensitive ? Color.Bisque : SystemColors.Control;
        }
    }
}